package metar;

import java.util.ArrayList;

import util.Config;

/**
 * Abbildung einer Metar-Info, wie sie vom Flughafen geliefert wird
 * @author Michel Metzger, Java-Kurs 2013
 * @version 1.0.0
 *
 */
public class MetarInfo {

	/**
	 * Im debugMode werden interne Testdaten in der Console ausgegeben
	 */
	private static boolean debugMode = new Boolean(Config.getStaticProperty("metardebug"));
	
	/**
	 * Metar-Description: 	http://de.wikipedia.org/wiki/METAR
	 * 						http://en.wikipedia.org/wiki/METAR
	 * 
	 * Exsamples:
	 * 
	 * 2013/06/04 12:50 
	 * EDDS 081620Z 29010KT 9999 FEW040TCU 09/M03 Q1012 NOSIG
	 * 
	 * 2013/06/05 07:20
	 * EDDS 050720Z 08004KT CAVOK 16/10 Q1018 NOSIG
	 * 
	 * 2013/06/11 10:20
	 * EDDS 111020Z 31004KT 4800 SHRA FEW016CB FEW022TCU BKN030 15/13 Q1018 NOSIG
	 * 
	 * 2013/06/14 08:08
	 * KLGA 140808Z 36010G17KT 10SM -RA FEW014 OVC031 13/11 A2961 RMK AO2 P0000
	 * 
	 * 
	 * EDDS = Flugplatzkennung, hier Flughafen Stuttgart
	 * 081620Z = Beobachtungszeitpunkt (08. des Monats, um 16:20 UTC also 17:20 MEZ bzw. 18:20 MESZ)
	 * 29010KT = Windrichtung bezogen auf rechtweisend Nord (290°, also WNW) und Windgeschwindigkeit (10 kn)
	 * 9999 = Sichtweite am Boden in m (hier also 10 km oder mehr) => Nicht zu verwechseln mit Pistensichtweite (RVR)!
	 * FEW040TCU = Bewölkung: Stärke (FEW = 1 bis 2 Achtel), Höhe der Wolkenuntergrenze (4.000 Fuß über Grund) und Art der Bew�lkung (TCU = towering cumulus)
	 * 09/M03 = Temperatur (9°C) und Taupunkt (M steht für negative Werte, also -3°C) 
	 * Q1012 = QNH von 1012 hPa, also der am Platz herrschende Luftdruck nach der Standardatmosph�re reduziert auf Meereshöhe beträgt 1012 Hektopascal
	 * NOSIG = ("no significant change") Vorhersage für die nächsten zwei Stunden 
	 * 
	 */
	
	/**
	 * aufgeschlüsselte Daten aus der Metar-Info 
	 */
	private String					retrievalDate;		// Datum und Uhrzeit des Abrufs
	private String					airportCode;		// 4-stellige Flugplatzkennung
	private ObservationDateMetar 	observationDate; 	// Beobachtungszeitpunkt
	private WindMetar				wind;				// Windrichtung und Geschwindigkeit
	private VisibilityMetar			visibility;			// Sichtweite in Meter
	private ArrayList<CloudsMetar>	clouds;				// Bewölkung und Wolkenuntergrenze (kann mehrfach vorkommen)
	private TemperatureMetar		temperature;		// Temperatur und Taupunkt		
	private PressureMetar			pressure;			// Luftdruck in hPa
	private TrendMetar				trend;				// Vorhersage für die nächsten Stunden

	/**
	 * Konstruktur
	 */
	public MetarInfo() {
		init();
	}
	
	/**
	 * Iniitialisierung der Datamember
	 */
	private void init() {
		retrievalDate = "";
		airportCode = "";
		observationDate = null; 
		wind = null;
		visibility = null;
		clouds = null;
		temperature = null;
		pressure = null;
		trend  = null;
	}
	
	/**
	 * Liefert das Abfragedatum
	 * @return
	 */
	public String getRetrievalDate() {
		return retrievalDate;
	}

	/**
	 * Liefert den Flughafencode
	 * @return
	 */
	public String getAirportCode() {
		return airportCode;
	}

	/**
	 * Liefert den Beobachtungszeitpunkt
	 * @return
	 */
	public ObservationDateMetar getObservationDate() {
		return observationDate;
	}

	/**
	 * Liefert die Windinformationen
	 * @return
	 */
	public WindMetar getWind() {
		return wind;
	}

	/**
	 * Liefert die Sichtweite in m
	 * @return 
	 * @return
	 */
	public VisibilityMetar getVisibility() {
		return visibility;
	}

	/**
	 * Liefert die Angaben zur Bewölkung
	 * @return 
	 * @return
	 */
	public ArrayList<CloudsMetar> getClouds() {
		return clouds;
		
	}
	
	/**
	 * Liefert die Temperatur in ° C
	 * @return 
	 * @return
	 */
	public TemperatureMetar getTemperature() {
		return temperature;
	}

	/**
	 * Liefert den Luftdruck
	 * @return 
	 * @return
	 */
	public PressureMetar getPressure() {
		return pressure;
	}

	/**
	 * Liefert eine Vorhersage für die nächsten 2 Stunden 
	 * @return
	 */
	public String getTrend() {
		return trend == null ? "" : trend.getTrend();
	}

	/**
	 * Ist der debugMode aktiviert?
	 * @return
	 */
	public static boolean isDebugMode() {
		return debugMode;
	}

	/**
	 * Setzt oder löscht den debugMode
	 * @param set
	 */
	public static void setDebugMode(boolean set) {
		debugMode = set;
	}
	
	/**
	 * Verarbeitung einer kompletten Metar-Info
	 * @param info
	 * @throws MetarParseException
	 */
	public void parse(String info) throws MetarParseException {
		
		/**
		 * Die parse-Methode kann mehrfach aufgerufen werden, deshalb hier nochmal init()
		 */
		init(); 
		
		if (info == null || info.isEmpty()) {
			throw new MetarParseException("Weather data is empty!");
		}
		
		if (isDebugMode()) {
			System.out.println("===============================================================");
			System.out.println("Metar: " + info);
			System.out.println("===============================================================");
		}
		
		/**
		 * Metarinfo in einzelne Zeilen aufsplitten, Trennzeichen ist \n
		 */
		String[] lines = info.split("\n");
		if (isDebugMode()) {
			for (int i = 0; i < lines.length; ++i) {
				System.out.println("line[" + i + "]=" + lines[i]);
			}
		}
		if (lines.length < 2) {
			throw new MetarParseException("Invalid weather data format (too few lines)!");
		}
		
		/**
		 * 2. Zeile in die einzelnen Parameter aufsplitten, Trennzeichen ist Blank
		 */
		String[] parts = lines[1].split(" ");
		if (isDebugMode()) {
			for (int i = 0; i < parts.length; ++i) {
				System.out.println("part[" + i + "]=" + parts[i]);
			}
		}
		if (parts.length < 5) {
			throw new MetarParseException("Invalid weather data format (too few parameters)!");
		}

		/**
		 * Die 1. Zeile entält das Abfragedatum
		 */
		retrievalDate = lines[0];
		if (MetarInfo.isDebugMode()) {
			System.out.println("*** date=" + retrievalDate);
		}
		
		/**
		 * Der 1. Parameter in der 2. Zeile ist der Flughafencode
		 */
		airportCode = parts[0];
		if (MetarInfo.isDebugMode()) {
			System.out.println("*** airportCode=" + airportCode);
		}

		/**
		 * Die einzelnen Parameter mit Hilfe der jeweiligen isValid-Methode 
		 * der einzelnen Subklassen überprüfen, ob der Parameter die passenden Daten 
		 * enthält und ggf. Übernahme in das passende Objekt der MetarInfo-Klasse
		 */
		try {
			for (int i = 1; i < parts.length; ++i) {
				
				if (ObservationDateMetar.isValid(parts[i])) {
					observationDate = new ObservationDateMetar().parseParam(parts[i]);
				} else if (WindMetar.isValid(parts[i])) {
					wind = new WindMetar().parseParam(parts[i]);
				} else if (wind != null && WindMetar.isValidVaryingExtension(parts[i])) {
					wind.parseParam(parts[i]);
				} else if (PressureMetar.isValid(parts[i])) {
					pressure = new PressureMetar().parseParam(parts[i]);
				} else if (VisibilityMetar.isValid(parts[i])) {
					visibility = new VisibilityMetar().parseParam(parts[i]);
				} else if (CloudsMetar.isValid(parts[i])) {
					/*
					 * clouds-Objekte können mehrfach vorkommen
					 */
					if (clouds == null) {
						clouds = new ArrayList<CloudsMetar>();
					}
					clouds.add(new CloudsMetar().parseParam(parts[i]));
				} else if (TemperatureMetar.isValid(parts[i])) {
					temperature = new TemperatureMetar().parseParam(parts[i]);
				} else if (TrendMetar.isValid(parts[i])) {
					trend = new TrendMetar().parseParam(parts[i]);
				}
			}
		} catch (Exception e) {
			if (MetarInfo.isDebugMode()) {
				e.printStackTrace();
			}
			throw new MetarParseException("Exception while parsing weather data!");
		}
	}

}
